<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>学生成绩管理系统</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        h1 {
            color: #333;
            text-align: center;
        }
        .container {
            background-color: white;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        th, td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid #ddd;
        }
        th {
            background-color: #f2f2f2;
            cursor: pointer;
        }
        th:hover {
            background-color: #e6e6e6;
        }
        tr:hover {
            background-color: #f9f9f9;
        }
        .student-name {
            color: #3498db;
            cursor: pointer;
            font-weight: bold;
        }
        .student-name:hover {
            text-decoration: underline;
        }
        .action-btn {
            padding: 5px 10px;
            margin-right: 5px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .delete-btn {
            background-color: #e74c3c;
            color: white;
        }
        .delete-btn:hover {
            background-color: #c0392b;
        }
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
        }
        .error {
            color: #e74c3c;
            font-size: 12px;
            margin-top: 5px;
        }
        button[type="submit"] {
            background-color: #2ecc71;
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button[type="submit"]:hover {
            background-color: #27ae60;
        }
        .modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 1000;
        }
        .modal-content {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            width: 400px;
            max-width: 90%;
        }
        .close-btn {
            float: right;
            cursor: pointer;
            font-size: 20px;
        }
        .scroll-container {
            height: 500px;
            overflow-y: auto;
            position: relative;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        .student-list {
            position: relative;
        }
        .student-row {
            position: absolute;
            width: 100%;
            box-sizing: border-box;
            padding: 12px 15px;
            border-bottom: 1px solid #ddd;
            display: flex;
            justify-content: space-between;
            align-items: center;
            background-color: white;
        }
        .student-row:hover {
            background-color: #f9f9f9;
        }
        .sort-indicator {
            margin-left: 5px;
        }
        .average-score {
            font-weight: bold;
            color: #2ecc71;
        }
    </style>
</head>
<body>
    <div id="app">
        <div class="container">
            <h1>学生成绩管理系统</h1>
            
            <!-- 添加学生表单 -->
            <div class="form-section">
                <h2>添加学生</h2>
                <form @submit.prevent="addStudent">
                    <div class="form-group">
                        <label for="name">姓名</label>
                        <input type="text" id="name" v-model="newStudent.name" required>
                        <div class="error" v-if="errors.name">{{ errors.name }}</div>
                    </div>
                    <div class="form-group">
                        <label for="math">数学成绩</label>
                        <input type="number" id="math" v-model="newStudent.math" min="0" max="100" required>
                        <div class="error" v-if="errors.math">{{ errors.math }}</div>
                    </div>
                    <div class="form-group">
                        <label for="english">英语成绩</label>
                        <input type="number" id="english" v-model="newStudent.english" min="0" max="100" required>
                        <div class="error" v-if="errors.english">{{ errors.english }}</div>
                    </div>
                    <button type="submit">添加学生</button>
                </form>
            </div>
            
            <!-- 学生列表 -->
            <div class="list-section">
                <h2>学生列表</h2>
                
                <!-- 普通表格视图 -->
                <div v-if="!useVirtualScroll">
                    <table>
                        <thead>
                            <tr>
                                <th @click="sortBy('id')">
                                    ID
                                    <span class="sort-indicator" v-if="sortField === 'id'">
                                        {{ sortDirection === 'asc' ? '↑' : '↓' }}
                                    </span>
                                </th>
                                <th @click="sortBy('name')">
                                    姓名
                                    <span class="sort-indicator" v-if="sortField === 'name'">
                                        {{ sortDirection === 'asc' ? '↑' : '↓' }}
                                    </span>
                                </th>
                                <th @click="sortBy('math')">
                                    数学
                                    <span class="sort-indicator" v-if="sortField === 'math'">
                                        {{ sortDirection === 'asc' ? '↑' : '↓' }}
                                    </span>
                                </th>
                                <th @click="sortBy('english')">
                                    英语
                                    <span class="sort-indicator" v-if="sortField === 'english'">
                                        {{ sortDirection === 'asc' ? '↑' : '↓' }}
                                    </span>
                                </th>
                                <th @click="sortBy('average')">
                                    平均分
                                    <span class="sort-indicator" v-if="sortField === 'average'">
                                        {{ sortDirection === 'asc' ? '↑' : '↓' }}
                                    </span>
                                </th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr v-for="student in sortedStudents" :key="student.id">
                                <td>{{ student.id }}</td>
                                <td>
                                    <span class="student-name" @click="showStudentDetail(student)">
                                        {{ student.name }}
                                    </span>
                                </td>
                                <td>{{ student.math }}</td>
                                <td>{{ student.english }}</td>
                                <td class="average-score">{{ calculateAverage(student).toFixed(1) }}</td>
                                <td>
                                    <button class="action-btn delete-btn" @click="deleteStudent(student.id)">
                                        删除
                                    </button>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
                
                <!-- 虚拟滚动视图 -->
                <div v-else>
                    <div class="scroll-container" @scroll="handleScroll" ref="scrollContainer">
                        <div class="student-list" :style="{ height: totalHeight + 'px' }">
                            <div 
                                class="student-row" 
                                v-for="student in visibleStudents" 
                                :key="student.id"
                                :style="{ top: student.top + 'px', height: rowHeight + 'px' }"
                            >
                                <span>
                                    <span style="width: 50px; display: inline-block;">{{ student.id }}</span>
                                    <span class="student-name" @click="showStudentDetail(student)" style="width: 100px; display: inline-block;">
                                        {{ student.name }}
                                    </span>
                                    <span style="width: 60px; display: inline-block;">数学: {{ student.math }}</span>
                                    <span style="width: 60px; display: inline-block;">英语: {{ student.english }}</span>
                                    <span class="average-score" style="width: 80px; display: inline-block;">
                                        平均: {{ calculateAverage(student).toFixed(1) }}
                                    </span>
                                </span>
                                <button class="action-btn delete-btn" @click="deleteStudent(student.id)">
                                    删除
                                </button>
                            </div>
                        </div>
                    </div>
                    <div style="margin-top: 10px;">
                        显示 {{ visibleStudents.length }} 名学生 (共 {{ students.length }} 名)
                    </div>
                </div>
                
                <div style="margin-top: 20px;">
                    <button @click="generateTestData" style="margin-right: 10px;">生成测试数据</button>
                    <button @click="useVirtualScroll = !useVirtualScroll">
                        {{ useVirtualScroll ? '切换到普通表格' : '切换到虚拟滚动' }}
                    </button>
                </div>
            </div>
            
            <!-- 学生详情弹窗 -->
            <div class="modal" v-if="selectedStudent" @click.self="selectedStudent = null">
                <div class="modal-content">
                    <span class="close-btn" @click="selectedStudent = null">&times;</span>
                    <h2>学生详情</h2>
                    <div style="margin-bottom: 10px;"><strong>ID:</strong> {{ selectedStudent.id }}</div>
                    <div style="margin-bottom: 10px;"><strong>姓名:</strong> {{ selectedStudent.name }}</div>
                    <div style="margin-bottom: 10px;"><strong>数学成绩:</strong> {{ selectedStudent.math }}</div>
                    <div style="margin-bottom: 10px;"><strong>英语成绩:</strong> {{ selectedStudent.english }}</div>
                    <div style="margin-bottom: 10px;">
                        <strong>平均分:</strong> 
                        <span class="average-score">{{ calculateAverage(selectedStudent).toFixed(1) }}</span>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        const { ref, computed, onMounted } = Vue;
        
        const app = Vue.createApp({
            setup() {
                // 学生数据
                const students = ref([
                    { id: 1, name: "张三", math: 90, english: 85 },
                    { id: 2, name: "李四", math: 78, english: 92 },
                    { id: 3, name: "王五", math: 88, english: 76 },
                    { id: 4, name: "赵六", math: 65, english: 82 },
                    { id: 5, name: "钱七", math: 72, english: 90 }
                ]);
                
                // 新学生表单
                const newStudent = ref({
                    name: '',
                    math: '',
                    english: ''
                });
                
                // 表单验证错误
                const errors = ref({
                    name: '',
                    math: '',
                    english: ''
                });
                
                // 选中的学生
                const selectedStudent = ref(null);
                
                // 排序字段和方向
                const sortField = ref('id');
                const sortDirection = ref('asc');
                
                // 虚拟滚动相关
                const useVirtualScroll = ref(false);
                const scrollContainer = ref(null);
                const scrollTop = ref(0);
                const rowHeight = 50; // 每行高度
                const buffer = 5; // 缓冲行数
                
                // 计算属性
                const sortedStudents = computed(() => {
                    return [...students.value].sort((a, b) => {
                        let valueA, valueB;
                        
                        if (sortField.value === 'average') {
                            valueA = calculateAverage(a);
                            valueB = calculateAverage(b);
                        } else {
                            valueA = a[sortField.value];
                            valueB = b[sortField.value];
                        }
                        
                        if (typeof valueA === 'string') {
                            return sortDirection.value === 'asc' 
                                ? valueA.localeCompare(valueB)
                                : valueB.localeCompare(valueA);
                        } else {
                            return sortDirection.value === 'asc' 
                                ? valueA - valueB
                                : valueB - valueA;
                        }
                    });
                });
                
                // 虚拟滚动计算属性
                const totalHeight = computed(() => students.value.length * rowHeight);
                const startIndex = computed(() => {
                    return Math.max(0, Math.floor(scrollTop.value / rowHeight) - buffer);
                });
                const endIndex = computed(() => {
                    const containerHeight = scrollContainer.value?.clientHeight || 0;
                    return Math.min(
                        students.value.length - 1,
                        Math.floor((scrollTop.value + containerHeight) / rowHeight) + buffer
                    );
                });
                const visibleStudents = computed(() => {
                    return sortedStudents.value
                        .slice(startIndex.value, endIndex.value + 1)
                        .map((student, index) => ({
                            ...student,
                            top: (startIndex.value + index) * rowHeight
                        }));
                });
                
                // 方法
                const calculateAverage = (student) => {
                    return (parseInt(student.math) + parseInt(student.english)) / 2;
                };
                
                const validateForm = () => {
                    let isValid = true;
                    errors.value = { name: '', math: '', english: '' };
                    
                    // 验证姓名
                    if (!newStudent.value.name.trim()) {
                        errors.value.name = '请输入学生姓名';
                        isValid = false;
                    }
                    
                    // 验证数学成绩
                    const mathScore = parseInt(newStudent.value.math);
                    if (isNaN(mathScore) || mathScore < 0 || mathScore > 100) {
                        errors.value.math = '请输入0-100之间的有效分数';
                        isValid = false;
                    }
                    
                    // 验证英语成绩
                    const englishScore = parseInt(newStudent.value.english);
                    if (isNaN(englishScore) || englishScore < 0 || englishScore > 100) {
                        errors.value.english = '请输入0-100之间的有效分数';
                        isValid = false;
                    }
                    
                    return isValid;
                };
                
                const addStudent = () => {
                    if (!validateForm()) return;
                    
                    // 生成新ID
                    const newId = students.value.length > 0 
                        ? Math.max(...students.value.map(s => s.id)) + 1 
                        : 1;
                    
                    students.value.push({
                        id: newId,
                        name: newStudent.value.name.trim(),
                        math: parseInt(newStudent.value.math),
                        english: parseInt(newStudent.value.english)
                    });
                    
                    // 重置表单
                    newStudent.value = { name: '', math: '', english: '' };
                };
                
                const deleteStudent = (id) => {
                    if (confirm('确定要删除这名学生吗？')) {
                        students.value = students.value.filter(student => student.id !== id);
                    }
                };
                
                const showStudentDetail = (student) => {
                    selectedStudent.value = student;
                };
                
                const sortBy = (field) => {
                    if (sortField.value === field) {
                        sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc';
                    } else {
                        sortField.value = field;
                        sortDirection.value = 'asc';
                    }
                };
                
                const handleScroll = () => {
                    if (scrollContainer.value) {
                        scrollTop.value = scrollContainer.value.scrollTop;
                    }
                };
                
                const generateTestData = () => {
                    const names = ['周八', '吴九', '郑十', '孙十一', '刘十二', '陈十三', '杨十四', '黄十五'];
                    const testCount = 50; // 生成50条测试数据
                    
                    for (let i = 0; i < testCount; i++) {
                        const newId = students.value.length > 0 
                            ? Math.max(...students.value.map(s => s.id)) + 1 
                            : 1;
                        
                        students.value.push({
                            id: newId,
                            name: names[Math.floor(Math.random() * names.length)] + (i + 1),
                            math: Math.floor(Math.random() * 101),
                            english: Math.floor(Math.random() * 101)
                        });
                    }
                };
                
                // 初始化
                onMounted(() => {
                    if (scrollContainer.value) {
                        scrollContainer.value.addEventListener('scroll', handleScroll);
                    }
                });
                
                return {
                    students,
                    newStudent,
                    errors,
                    selectedStudent,
                    sortField,
                    sortDirection,
                    useVirtualScroll,
                    scrollContainer,
                    scrollTop,
                    rowHeight,
                    totalHeight,
                    visibleStudents,
                    sortedStudents,
                    calculateAverage,
                    addStudent,
                    deleteStudent,
                    showStudentDetail,
                    sortBy,
                    handleScroll,
                    generateTestData
                };
            }
        });
        
        app.mount('#app');
    </script>
</body>
</html>1